package org.putrpctorest.parameter.impl;

import org.putrpctorest.parameter.impl.log.MatchLog;

public class Matcher {

	private Definitions defs1;

	private Definitions defs2;

	private MatchLog matchLog;

	public Matcher(Definitions params1,

	Definitions params2, MatchLog matchLog) {
		this.defs1 = params1;
		this.defs2 = params2;
		this.matchLog = matchLog;
	}

	public Match tryMatch() {
		matchLog.startMatch(defs1, defs2);
		Match match;
		if (bothDefsHaveSameSize(matchLog)) {
			match = tryNameCorrespondence();
			if (match != Match.NO_MATCH) {
				return match;
			}
			match = tryTypeCorrespondence();
			if (match != Match.NO_MATCH) {
				return match;
			}
			return Match.NO_MATCH;
		} else {
			return Match.NO_MATCH;
		}
	}

	private Match tryTypeCorrespondence() {
		if (parameterHaveInterchangeableTypes()) {
			return tryCorrespondence(CORRESPONDENCE.TYPE);
		} else {
			return Match.NO_MATCH;
		}
	}

	private Match tryNameCorrespondence() {
		return tryCorrespondence(CORRESPONDENCE.NAME);
	}

	private boolean parameterHaveInterchangeableTypes() {
		return (!hasInterchangeableTypes(defs1))
				&& (!hasInterchangeableTypes(defs2));
	}

	private boolean hasInterchangeableTypes(Definitions params) {
		Definition[] interchangeableTypes = params.getInterchangeableTypes();
		if (interchangeableTypes == null) {
			return false;
		} else {
			matchLog.definitionHasInterchangeableTypes(interchangeableTypes[0],
					interchangeableTypes[1]);
			return true;

		}
	}

	private Match tryCorrespondence(CORRESPONDENCE correspondence) {
		Expressions defs1ToDefs2Expressions = new Expressions(
				defs1.getDescription());
		Expressions defs2ToDefs1Expressions = new Expressions(
				defs2.getDescription());
		for (Definition def1 : defs1) {
			boolean matchWasFound = false;
			for (Definition def2 : defs2) {
				
				
				if (def1.getCorrespondences(def2).has(correspondence)) {
					
					defs1ToDefs2Expressions.addAllNew(def2.getRootExpressions());
					defs2ToDefs1Expressions.addAllNew(def1.getRootExpressions());
					
					defs1ToDefs2Expressions.add(def1.to(def2));
					defs2ToDefs1Expressions.add(def2.to(def1));

					matchWasFound = true;
					break;
				}
			}
			if (!matchWasFound) {
				matchLog.correspondenceWasNotPossible(correspondence);
				return Match.NO_MATCH;
			}
		}
		return Match.createMatch(defs1ToDefs2Expressions,
				defs2ToDefs1Expressions);
	}

	private boolean bothDefsHaveSameSize(MatchLog matchLog) {
		if (defs1.sameSize(defs2)) {
			return true;
		} else {
			matchLog.diferentSizeParamters();
			return false;
		}
	}

}
